<?php

use think\Exception;
use think\facade\Session;
use think\db\exception\ModelNotFoundException;
use think\helper\Str;

/**
 * 权限管理
 *
 * @author RenLiang Sun
 */
class Auth
{

    // 公共资源
    const COMMEN = '*';

    // 受保护资源
    const PROTECT = '-';

    // 错误
    public $error = '';

    public function __construct()
    {

    }

    /**
     * 登录
     *
     * @param array $data
     *
     * @return bool
     */
    public function login(array $data)
    {
        $model = new app\model\Auth();
        try {
            $result = $model->getAdminData($data);

            // 排除敏感数据
            unset($result->password, $result->status);
            // 设置数据
            Session::set('account', $result->toArray());

            return true;
        } catch (ModelNotFoundException $e) {
            $this->setError('帐号不存在');
        } catch (Exception $e) {
            $this->setError($e->getMessage());
        }
        return false;
    }

    /**
     * 管理员信息
     *
     * @param string|null $key 唯一标识
     *
     * @return mixed
     */
    public function info(string $key = null)
    {
        if (is_null($key)) {
            return isset($this->user) ? $this->user : Session::get('account');
        } else {
            return Session::get('account.'.$key);
        }
    }

    /**
     * 是否登录
     */
    public function isLogin()
    {
        $admin = Session::get('account');
        if (!$admin) {
            return false;
        }
        return true;
    }

    /**
     * 设置错误
     *
     * @param string $error 错误信息
     *
     * @return $this
     */
    private function setError(string $error)
    {
        $this->error = $error;
        return $this;
    }

    /**
     * 错误
     * @return string
     */
    public function error()
    {
        return $this->error;
    }

    /**
     * 处理PathInfo，并返回
     *
     * @param string $pathInfo
     *
     * @return string
     */
    public function getPathInfo(string $pathInfo)
    {
        if (false != ($position = strpos($pathInfo, '.'))){
            $pathInfo = Str::substr($pathInfo, 0, $position);
        }
        return '/'.$pathInfo;
    }

    /**
     * 退出
     */
    public function logout()
    {
        Session::delete('account');
    }

    /**
     * 获取用户菜单
     *
     * @param int|null    $uid
     * @param string|null $pathInfo
     *
     * @return array
     */
    public function getNavMenu(int $uid = null, string $pathInfo = null)
    {
        if (is_null($uid)) {
            $uid = $this->info('id');
        }
        if (is_null($pathInfo)) {
            $pathInfo = $this->getPathInfo(app('request')->pathinfo());
        }
        $model = new \app\model\Auth();
        // 根据pathInfo获取对应规则
        $self = $model->getRuleByPathInfo($pathInfo);
        // 获取当前用户分配的角色
        $ruleIds = $this->getRuleIds($uid);
        $isSuper = $ruleIds[0] == '*';
        // 获取所属规则列表和面包屑导航数据
        list($list, $crumb) = $model->getRuleListAndCrumb($uid, $ruleIds, $isSuper);
        tree()->init($crumb);
        // 格式化面包屑数据
        $crumb = tree()->getParents($self['id'], true);
        // 父类id数组
        $parent_ids = [$self['id']];
        foreach ($crumb as $val) {
            array_push($parent_ids, $val['id']);
        }
        // 格式化menu数据
        $menu = Arraye::list_to_tree($list, 'id', 'pid', 'child');
        $sort_by_sort = function($x, $y) {
            if ($x['sort'] == $y['sort']) {
                return 0;
            }
            return ($x['sort'] < $y['sort']) ? -1 : 1;
        };
        uasort($menu, $sort_by_sort);
        $data = [
            'menu' => $menu,
            'crumb' => $crumb,
            'pids' => $parent_ids,
            'self' => $self,
            'path' => $this->getPathInfo(request()->pathinfo()),
            'loadJsPath' => $this->getLoadJsPath()
        ];
        return $data;
    }

    /**
     * 获取规则id
     *
     * @param int|null $uid 管理员id
     *
     * @return array
     */
    protected function getRuleIds(int $uid = null): array
    {
        $uid = is_null($uid) ? $this->info('id') : $uid;
        //读取用户所属用户组
        $groups = $this->getGroups($uid);
        $ids = []; //保存用户所属用户组设置的所有权限规则id
        foreach ($groups as $g) {
            $ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
        }
        $ids = array_unique($ids);
        return $ids;
    }

    /**
     * 根据用户id获取角色组
     *
     * @param int|null $uid
     *
     * @return array 管理员所属的角色组 [['uid' => '用户id', 'group_id'=>'用户组id', 'name'=>'用户组名称', 'rules'=>'用户组拥有的规id]]
     */
    public function getGroups(int $uid = null): array
    {
        $uid = is_null($uid) ? $this->info('id') : $uid;
        static $groups = [];
        if (isset($groups[$uid])) {
            return $groups[$uid];
        }
        $model = new \app\model\Auth();
        // 获取用户角色
        $userRole = $model->getAdminRole($uid);
        $groups[$uid] = $userRole ?: [];
        return $groups[$uid];
    }

    /**
     * 获取加载js文件地址
     *
     * @return string
     */
    private function getLoadJsPath(): array
    {
        // 控制器名称
        $name = request()->controller(true);
        // 加载js文件的路径地址
        $loadJsPath = str_replace('.', '/', $name);
        // 获取请求方法
        $action = request()->action(true);
        // 文件数据
        $fileResult = [
            'file' => strtolower($loadJsPath.'/'.$action), 'result' => true
        ];
        $assets = config('view.tpl_replace_string.__ASSETS__').'/js';
        if (!file_exists('.'.$assets.'/'.$loadJsPath.'/'.$action.'.js')) {
            $fileResult['result'] = false;
        }
        // 返回数据
        return $fileResult;
    }

    /**
     * 判断是否是超级管理员
     *
     * @return bool
     */
    public function isSuperAdmin(): bool
    {
        return in_array('*', $this->getRuleIds()) ? true : false;
    }

    /**
     * 取出当前管理员所拥有权限的管理员
     *
     * @param bool $withSelf 是否包含自己
     *
     * @return array
     */
    public function getChildrenAdminIds(bool $withSelf = false): array
    {
        $childrenAdminIds = [];
        $model = new \app\model\Auth();
        if (!$this->isSuperAdmin()) {
            // 子类角色列表
            $roleIds = $this->getChildrenGroupIds(false);
            // 获取权限角色表
            $authRoleList = $model->getAuthRoleList($roleIds);
            foreach ($authRoleList as $k => $v) {
                $childrenAdminIds[] = $v['uid'];
            }
        } else {
            // 超级管理员拥有所有人的权限
            $childrenAdminIds = $model->getAllAdminId();
        }
        if ($withSelf) {
            if (!in_array($this->info('id'), $childrenAdminIds)) {
                $childrenAdminIds[] = $this->info('id');
            }
        } else {
            $childrenAdminIds = array_diff($childrenAdminIds, [$this->info('id')]);
        }
        return $childrenAdminIds;
    }

    /**
     * 取出当前管理员所拥有权限的分组
     *
     * @param bool $withSelf 是否包含当前所在的分组
     *
     * @return array
     */
    public function getChildrenGroupIds(bool $withSelf = false): array
    {
        // 取出当前管理员所分配的所有角色组
        $groups = $this->getGroups();
        $groupIds = [];
        foreach ($groups as $k => $v) {
            $groupIds[] = $v['id'];
        }
        $model = new \app\model\Auth();
        // 取出所有分组
        $groupList = $model->getAllAuthRole();
        $objList = [];
        foreach ($groups as $K => $v) {
            if ($v['rules'] === '*') {
                $objList = $groupList;
                break;
            }
            // 取出包含自己的所有子节点
            $childrenList = tree()->init($groupList)->getChildren($v['id'], true);
            $obj = tree()->init($childrenList)->getTreeArray($v['pid']);
            $objList = array_merge($objList, tree()->getTreeList($obj));
        }
        $childrenGroupIds = [];
        foreach ($objList as $k => $v) {
            $childrenGroupIds[] = $v['id'];
        }
        if (!$withSelf) {
            $childrenGroupIds = array_diff($childrenGroupIds, $groupIds);
        }
        return $childrenGroupIds;
    }

}
